/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mario.game.graymachine.core;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;

import mario.game.graymachine.sprite.GLSprite;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.os.Message;
import android.util.Log;

/**
 * An OpenGL ES renderer based on the GLSurfaceView rendering framework.  This
 * class is responsible for drawing a list of renderables to the screen every
 * frame.  It also manages loading of textures and (when VBOs are used) the
 * allocation of vertex buffer objects.
 */
public class SimpleGLRenderer implements GLSurfaceView.Renderer {
    // Specifies the format our textures should be converted to upon load.
    private static BitmapFactory.Options sBitmapOptions
        = new BitmapFactory.Options();
    // An array of things to draw every frame.
//    private GLSprite[] mSprites;
    private Scene mScene;
    // Pre-allocated arrays to use at runtime so that allocation during the
    // test can be avoided.
    private int[] mTextureNameWorkspace;
    private int[] mCropWorkspace;
    // A reference to the application context.
    private Context mContext;
	private HashMap<Integer, Integer> bitMapHash=new HashMap<Integer, Integer>();
    
    public SimpleGLRenderer(Context context) {
        // Pre-allocate and store these objects so we can use them at runtime
        // without allocating memory mid-frame.
        mTextureNameWorkspace = new int[1];
        mCropWorkspace = new int[4];
        
        // Set our bitmaps to 16-bit, 565 format.
        sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        
        mContext = context;
    }
    
    public int[] getConfigSpec() {
        // We don't need a depth buffer, and don't care about our
        // color depth.
        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_NONE };
        return configSpec;
    }

    /** Draws the sprites. */
    public void drawFrame(GL10 gl) {
        if (mScene.mSprites != null) {

            gl.glMatrixMode(GL10.GL_MODELVIEW);
            for(int x=0;x<mScene.bg1.length;x++){
            	mScene.bg1[x].draw(gl);
            }
            for (int x = 0; x < mScene.MAX_SPRITS; x++) {
            	if(mScene.mSprites[x]!=null&&!mScene.mSprites[x].isDead){
            		mScene.mSprites[x].draw(gl);
            	}
            }
            mScene.player.draw(gl);
            for(int x=0; x<mScene.MAX_BULLETS;x++){
            	if(mScene.mBullets[x]!=null&&!mScene.mBullets[x].isDead){
            		mScene.mBullets[x].draw(gl);
            	}
            }
        }
    }

    /* Called when the size of the window changes. */
    public void sizeChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);

        /*
         * Set our projection matrix. This doesn't have to be done each time we
         * draw, but usually a new projection needs to be set when the viewport
         * is resized.
         */
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glOrthof(0.0f, width, 0.0f, height, 0.0f, 1.0f);
        
        gl.glShadeModel(GL10.GL_FLAT);
        gl.glEnable(GL10.GL_BLEND);
        gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
        gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
        gl.glEnable(GL10.GL_TEXTURE_2D);
    }

    /**
     * Called whenever the surface is created.  This happens at startup, and
     * may be called again at runtime if the device context is lost (the screen
     * goes to sleep, etc).  This function must fill the contents of vram with
     * texture data and (when using VBOs) hardware vertex arrays.
     */
    public void surfaceCreated(GL10 gl) {
         /*
         * Some one-time OpenGL initialization can be made here probably based
         * on features of this particular context
         */
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

        gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
        gl.glShadeModel(GL10.GL_FLAT);
        gl.glDisable(GL10.GL_DEPTH_TEST);
        gl.glEnable(GL10.GL_TEXTURE_2D);
        /*
         * By default, OpenGL enables features that improve quality but reduce
         * performance. One might want to tweak that especially on software
         * renderer.
         */
        gl.glDisable(GL10.GL_DITHER);
        gl.glDisable(GL10.GL_LIGHTING);

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        
        loadSceneImageResource(gl);
        
    }
    /**
     * load resource,may take a long time
     * @param gl
     */
    private void loadSceneImageResource(GL10 gl) {
		for(int i=0;i<mScene.bg1.length;i++){
			mScene.bg1[i].setTextureName(loadBitmap(mContext, gl, mScene.bg1[i].getResourceId()));
//			Log.d("backg:"+ mScene.bg1[i].getResourceId(), mScene.bg1[i].getTextureName()+"");
		}
		for(int i =0;i<mScene.mSpritesRes.length;i++){
			if(mScene.mSpritesRes[i]==null){
				continue;
			}
			int[][] resources=mScene.mSpritesRes[i].getResourceIds();
			if(resources!=null){
				for(int x=0;x<resources.length;x++){
					for(int y=0;y<resources[0].length;y++){
						if(resources[x][y]!=0){
							mScene.mSpritesRes[i].getTextureNames()[x][y]=loadBitmap(mContext, gl, resources[x][y]);
//							Log.d("res["+x+"]["+y+"]:"+resources[x][y], mScene.mSpritesRes[i].getTextureNames()[x][y]+"");
						}
					}
				}
			}else{
				if(mScene.mSpritesRes[i].getResourceId()!=0){
					mScene.mSpritesRes[i].setTextureName(loadBitmap(mContext, gl, mScene.mSpritesRes[i].getResourceId()));
				}
			}
//			Log.d(mScene+": "+mScene.mSpritesRes[i], mScene.mSpritesRes[i].getTextureNames()+"");
		}
//		for(int i =0;i<mScene.mBullets.length;i++){
//			final int[][] resources=mScene.mBullets[i].getResourceIds();
//			if(resources!=null){
//				final int[][] textures= new int[resources.length][resources[0].length];
//				for(int x=0;x<resources.length;x++){
//					for(int y=0;y<resources[y].length;y++){
//						textures[x][y]=loadBitmap(mContext, gl, resources[x][y]);
//					}
//				}
//				mScene.mBullets[i].setTextureNames(textures);
//			}else{
//				if(mScene.mBullets[i].getResourceId()!=0){
//					mScene.mBullets[i].setTextureName(loadBitmap(mContext, gl, mScene.mBullets[i].getResourceId()));
//				}
//			}
//		}
	}

	/**
     * Called when the rendering thread shuts down.  This is a good place to
     * release OpenGL ES resources.
     * @param gl
     */
    public void shutdown(GL10 gl) {
//        if (mSprites != null) {
//
//            int lastFreedResource = -1;
//            int[] textureToDelete = new int[1];
//            
//            for (int x = 0; x < mSprites.length; x++) {
//                int resource = mSprites[x].getResourceId();
//                if (resource != lastFreedResource) {
//                    textureToDelete[0] = mSprites[x].getTextureName();
//                    gl.glDeleteTextures(1, textureToDelete, 0);
//                    mSprites[x].setTextureName(0);
//                }
//            }
//        }
//    	for(GLSprite a:mScene.mSpritesRes){
//    		int[][] resources=a.getResourceIds();
//    		for(int[] inx: resources){
//    			for( int iny: inx){
//    				gl.glDeleteTextures(1,new int[]{iny},0);
//    				
//    			}
//    		}
//    	}
    }
 
    /** 
     * Loads a bitmap into OpenGL and sets up the common parameters for 
     * 2D texture maps. 
     */
    protected int loadBitmap(Context context, GL10 gl, int resourceId) {
    	if(bitMapHash.get(resourceId)==null){
	        int textureName = -1;
	        if (context != null && gl != null) {
	            gl.glGenTextures(1, mTextureNameWorkspace, 0);
	
	            textureName = mTextureNameWorkspace[0];
	            gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
	
	            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
	            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
	
	            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
	            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
	
	            gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
	
	            InputStream is = context.getResources().openRawResource(resourceId);
	            Bitmap bitmap;
	            try {
	                bitmap = BitmapFactory.decodeStream(is, null, sBitmapOptions);
	            } finally {
	                try {
	                    is.close();
	                } catch (IOException e) {
	                    // Ignore.
	                }
	            }
	
	            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
	
	            mCropWorkspace[0] = 0;
	            mCropWorkspace[1] = bitmap.getHeight();
	            mCropWorkspace[2] = bitmap.getWidth();
	            mCropWorkspace[3] = -bitmap.getHeight();
//	            Log.d("bitmap", "w"+ bitmap.getWidth()+"h"+ bitmap.getHeight());
	            bitmap.recycle();
	
	            ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, 
	                    GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0);
	
	            
	            int error = gl.glGetError();
	            if (error != GL10.GL_NO_ERROR) {
	                Log.e("g-ray", "Texture Load GLError: " + error);
	            }
	        
	        }
	        bitMapHash.put(resourceId,textureName);
    	}

        return bitMapHash.get(resourceId);
    }

	public void setScene(Scene scene) {
		// TODO Auto-generated method stub
		mScene=scene;
	}

}
